source('../env.R')

Species in communities

It seems reasonable to expect that cities with simialr regional pools will have similar species entering the city, and thus a similar response to urbanisation.

Load data

communities = read_csv(filename(COMMUNITY_OUTPUT_DIR, 'communities_for_analysis.csv'))
Rows: 2462 Columns: 9── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (5): city_name, jetz_species_name, seasonal, presence, origin
dbl (1): city_id
lgl (3): present_urban_high, present_urban_med, present_urban_low
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
communities_summary = communities %>% group_by(city_id) %>% summarise(
  regional_pool_size = n(), 
  urban_size_high = sum(present_urban_high), 
  urban_size_med = sum(present_urban_med), 
  urban_size_low = sum(present_urban_low)
)

communities_summary_long = bind_rows(
  communities_summary %>% rename(urban_pool_size = 'urban_size_high') %>% dplyr::select(city_id, regional_pool_size, urban_pool_size) %>% mutate(is_urban_threshold = 'High'),
  communities_summary %>% rename(urban_pool_size = 'urban_size_med') %>% dplyr::select(city_id, regional_pool_size, urban_pool_size) %>% mutate(is_urban_threshold = 'Medium'),
  communities_summary %>% rename(urban_pool_size = 'urban_size_low') %>% dplyr::select(city_id, regional_pool_size, urban_pool_size) %>% mutate(is_urban_threshold = 'Low')
)
communities_summary_long
city_points = st_centroid(read_sf(filename(CITY_DATA_OUTPUT_DIR, 'city_selection.shp')))
Warning: st_centroid assumes attributes are constant over geometries of xWarning: st_centroid does not give correct centroids for longitude/latitude data
community_data_metrics = read_csv(filename(COMMUNITY_OUTPUT_DIR, 'community_assembly_metrics.csv')) %>%
  dplyr::select(city_id, mntd_normalised, fd_normalised, urban_pool_size, is_urban_threshold) %>%
  left_join(read_csv(filename(CITY_DATA_OUTPUT_DIR, 'realms.csv'))) %>%
  left_join(communities_summary_long) %>%
  left_join(city_points[,c('city_id', 'city_nm')]) %>%
  rename(city_name='city_nm') %>%
  mutate(is_urban_threshold = factor(is_urban_threshold, levels = c('low', 'medium', 'high'), labels = c('Low', 'Medium', 'High'), ordered = T)) %>%
  arrange(city_id)
Rows: 792 Columns: 93── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr  (1): is_urban_threshold
dbl (92): city_id, mntd_normalised, mntd_actual, mntd_min, mntd_max, mntd_mean, mntd_sd, fd_normalised, fd_actual, fd_min, fd_max, fd_mean, fd_sd, FRic_normal...
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.Rows: 342 Columns: 2── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): core_realm
dbl (1): city_id
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.Joining with `by = join_by(city_id)`Joining with `by = join_by(city_id, urban_pool_size, is_urban_threshold)`Joining with `by = join_by(city_id)`
community_data_metrics

Load trait data

traits = read_csv(filename(TAXONOMY_OUTPUT_DIR, 'traits_jetz.csv'))
Rows: 304 Columns: 5── Column specification ──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
Delimiter: ","
chr (1): jetz_species_name
dbl (4): gape_width, trophic_trait, locomotory_trait, mass
ℹ Use `spec()` to retrieve the full column specification for this data.
ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
head(traits)
fetch_normalised_traits = function(required_species_list) {
  required_traits = traits %>% filter(jetz_species_name %in% required_species_list)
  
  required_traits$gape_width_normalised = normalise(required_traits$gape_width, min(required_traits$gape_width), max(required_traits$gape_width))
  required_traits$trophic_trait_normalised = normalise(required_traits$trophic_trait, min(required_traits$trophic_trait), max(required_traits$trophic_trait))
  required_traits$locomotory_trait_normalised = normalise(required_traits$locomotory_trait, min(required_traits$locomotory_trait), max(required_traits$locomotory_trait))
  required_traits$mass_normalised = normalise(required_traits$mass, min(required_traits$mass), max(required_traits$mass))
  
  traits_normalised_long = required_traits %>% pivot_longer(cols = c('gape_width_normalised', 'trophic_trait_normalised', 'locomotory_trait_normalised', 'mass_normalised'), names_to = 'trait', values_to = 'normalised_value') %>% dplyr::select(jetz_species_name, trait, normalised_value)
  traits_normalised_long$trait = factor(traits_normalised_long$trait, levels = c('gape_width_normalised', 'trophic_trait_normalised', 'locomotory_trait_normalised', 'mass_normalised'), labels = c('Gape Width', 'Trophic Trait', 'Locomotory Trait', 'Mass'))
  
  traits_normalised_long
}

fetch_normalised_traits(c('Aplopelia_larvata', 'Chalcophaps_indica', 'Caloenas_nicobarica'))

Read in our phylogeny

phylo_tree = read.tree(filename(TAXONOMY_OUTPUT_DIR, 'phylogeny.tre'))
ggtree(phylo_tree, layout='circular')

Load resolve ecoregions

resolve = read_resolve()
Warning: st_buffer does not correctly buffer longitude/latitude datadist is assumed to be in decimal degrees (arc_degrees).
Warning: st_simplify does not correctly simplify longitude/latitude data, dTolerance needs to be in decimal degrees

Create helper functions

to_species_matrix = function(filtered_communities) {
  filtered_communities %>% 
    dplyr::select(city_id, jetz_species_name) %>% 
    distinct() %>%
    mutate(present = TRUE) %>% 
    pivot_wider(
      names_from = jetz_species_name, 
      values_from = "present", 
      values_fill = list(present = F)
    ) %>% 
    tibble::column_to_rownames(var='city_id')
}
community_nmds = function(filtered_communities) {
  species_matrix = to_species_matrix(filtered_communities)
  nmds <- metaMDS(species_matrix, k=2, trymax = 30)
  nmds_result = data.frame(scores(nmds))
  nmds_result$city_id = as.double(rownames(scores(nmds)))
  rownames(nmds_result) = NULL
  
  nmds_result
}

https://www.datacamp.com/tutorial/k-means-clustering-r

scree_plot = function(community_nmds_data) {
  # Decide how many clusters to look at
  n_clusters <- min(10, nrow(community_nmds_data) - 1)
  
  # Initialize total within sum of squares error: wss
  wss <- numeric(n_clusters)
  
  set.seed(123)
  
  # Look over 1 to n possible clusters
  for (i in 1:n_clusters) {
    # Fit the model: km.out
    km.out <- kmeans(community_nmds_data[,c('NMDS1','NMDS2')], centers = i, nstart = 20)
    # Save the within cluster sum of squares
    wss[i] <- km.out$tot.withinss
  }
  
  # Produce a scree plot
  wss_df <- tibble(clusters = 1:n_clusters, wss = wss)
   
  scree_plot <- ggplot(wss_df, aes(x = clusters, y = wss, group = 1)) +
      geom_point(size = 4) +
      geom_line() +
      geom_hline(linetype="dashed", color = "orange", yintercept = wss) +
      scale_x_continuous(breaks = c(2, 4, 6, 8, 10)) +
      xlab('Number of clusters')
  scree_plot
}
cluster_cities = function(city_nmds, cities_community_data, centers) {
  set.seed(123)
  kmeans_clusters <- kmeans(city_nmds[,c('NMDS1', 'NMDS2')], centers = centers, nstart = 20)
  city_nmds$cluster = kmeans_clusters$cluster
  cities_community_data %>% left_join(city_nmds) %>% mutate(cluster = as.factor(cluster))
}
plot_nmds_clusters = function(cluster_cities) {
  cluster_cities %>% dplyr::select(city_id, city_name, NMDS1, NMDS2, cluster) %>% distinct() %>%
  ggplot(aes(x = NMDS1, y = NMDS2, colour = cluster)) + geom_point() + geom_label_repel(aes(label = city_name))
}
plot_city_cluster = function(city_cluster_data_metrics, title) {
  species_in_cluster = communities %>% 
    filter(city_id %in% city_cluster_data_metrics$city_id) %>% 
    group_by(jetz_species_name, city_name, present_urban_high, present_urban_med, present_urban_low) %>% 
    summarise() %>%
    mutate(present_score = present_urban_high + present_urban_med + present_urban_low)

  species_in_cluster$present_score = factor(species_in_cluster$present_score, levels = c(0, 1, 2, 3), labels = c('Regional Pool', 'Low Threshold', 'Medium Threshold', 'High Threshold'))
  
  tree_cropped <- ladderize(drop.tip(phylo_tree, setdiff(phylo_tree$tip.label, species_in_cluster$jetz_species_name)))
    
  gg_tree = ggtree(tree_cropped)
  
  gg_presence = ggplot(species_in_cluster, aes(x=city_name, y=jetz_species_name)) + 
          geom_tile(aes(fill=present_score)) + scale_fill_manual(values = c("green", "yellow", "orange", "red")) + 
          theme_minimal() + xlab(NULL) + ylab(NULL) + 
          theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1)) + 
          labs(fill='Presence')
  
  species_in_cluster_traits = fetch_normalised_traits(species_in_cluster$jetz_species_name)
  
  gg_traits = ggplot(species_in_cluster_traits, aes(x = trait, y = jetz_species_name, size = normalised_value)) + geom_point() + theme_minimal() + theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1), axis.text.y=element_blank()) + xlab(NULL) + ylab(NULL) + labs(size = "Normalised Value")
  
  gg_cities_mntd = ggplot(city_cluster_data_metrics, aes(x = city_name, y = mntd_normalised, fill = is_urban_threshold)) + geom_bar(stat = "identity", position = position_dodge(preserve = "single")) + scale_fill_manual(values = c("yellow", "orange", "red"))  + theme_minimal() + theme(legend.position = "none", axis.text.x=element_blank()) + xlab(NULL) + ylab("MNTD") + ylim(0, 1)
  
  gg_cities_fd = ggplot(city_cluster_data_metrics, aes(x = city_name, y = fd_normalised, fill = is_urban_threshold)) + geom_bar(stat = "identity", position = position_dodge(preserve = "single")) + scale_fill_manual(values = c("yellow", "orange", "red")) + theme_minimal() + theme(legend.position = "none", axis.text.x=element_blank()) + xlab(NULL) + ylab("FD") + ylim(0, 1)
  
  gg_title = ggplot() + labs(title = title) + theme_minimal()
  
  gg_presence %>% insert_top(gg_cities_mntd, height = 0.5) %>% insert_top(gg_cities_fd, height = 0.5) %>% insert_left(gg_tree, width = 0.75) %>% insert_right(gg_traits, width = 0.5) %>% insert_top(gg_title, height = 0.06)
}
REGION_DEEP_DIVE_FIGURES_OUTPUT = mkdir(FIGURES_OUTPUT_DIR, 'appendix_regional_deep_dive')

Nearctic

nearctic_cities_community_data = community_data_metrics %>% filter(core_realm == 'Nearctic')
nearctic_cities_community_data %>% dplyr::select(city_name) %>% distinct() %>% as.list()
$city_name
 [1] "San Jose"                 "Los Angeles"              "Concord"                  "Tijuana"                  "Bakersfield"             
 [6] "Fresno"                   "Sacramento"               "Mexicali"                 "Hermosillo"               "Las Vegas"               
[11] "Phoenix"                  "Tucson"                   "Durango"                  "Portland"                 "Chihuahua"               
[16] "Aguascalientes"           "Seattle"                  "Ciudad Juárez"            "San Luis Potosí"          "Mexico City"             
[21] "Saltillo"                 "Vancouver"                "Albuquerque"              "Monterrey"                "Nuevo Laredo"            
[26] "San Antonio"              "Austin"                   "Houston"                  "Dallas"                   "Oklahoma City"           
[31] "Calgary"                  "New Orleans"              "Kansas City"              "Omaha"                    "St. Louis"               
[36] "Bradenton"                "Tampa"                    "Minneapolis [Saint Paul]" "Atlanta"                  "Orlando"                 
[41] "Louisville"               "Chicago"                  "Indianapolis"             "Milwaukee"                "Virginia Beach"          
[46] "New York"                
nearctic_cities_nmds = community_nmds(communities %>% filter(city_id %in% nearctic_cities_community_data$city_id)) 
Run 0 stress 0.1161938 
Run 1 stress 0.1152814 
... New best solution
... Procrustes: rmse 0.03018951  max resid 0.1758497 
Run 2 stress 0.1152814 
... New best solution
... Procrustes: rmse 0.00004112701  max resid 0.0002340076 
... Similar to previous best
Run 3 stress 0.2542273 
Run 4 stress 0.1448163 
Run 5 stress 0.1198631 
Run 6 stress 0.1497522 
Run 7 stress 0.162113 
Run 8 stress 0.1152814 
... New best solution
... Procrustes: rmse 0.000008203033  max resid 0.00002624385 
... Similar to previous best
Run 9 stress 0.1145839 
... New best solution
... Procrustes: rmse 0.04424235  max resid 0.1777742 
Run 10 stress 0.1602924 
Run 11 stress 0.1148409 
... Procrustes: rmse 0.04440444  max resid 0.1744703 
Run 12 stress 0.1180524 
Run 13 stress 0.1176404 
Run 14 stress 0.1148408 
... Procrustes: rmse 0.04441218  max resid 0.1745631 
Run 15 stress 0.1145839 
... New best solution
... Procrustes: rmse 0.000007210488  max resid 0.00001868172 
... Similar to previous best
Run 16 stress 0.1180524 
Run 17 stress 0.1161938 
Run 18 stress 0.1148409 
... Procrustes: rmse 0.04440316  max resid 0.1744719 
Run 19 stress 0.114161 
... New best solution
... Procrustes: rmse 0.005102649  max resid 0.02607172 
Run 20 stress 0.1152814 
Run 21 stress 0.114161 
... Procrustes: rmse 0.000005376852  max resid 0.00001889389 
... Similar to previous best
*** Solution reached
nearctic_cities_nmds
scree_plot(nearctic_cities_nmds)

nearctic_cities = cluster_cities(city_nmds = nearctic_cities_nmds, cities_community_data = nearctic_cities_community_data, centers = 5)
Joining with `by = join_by(city_id)`
plot_nmds_clusters(nearctic_cities)

nearctic_biomes = st_crop(resolve[resolve$REALM == 'Nearctic',c('REALM')], xmin = -220, ymin = 0, xmax = 0, ymax = 70)
although coordinates are longitude/latitude, st_intersection assumes that they are planar
Warning: attribute variables are assumed to be spatially constant throughout all geometries
 
ggplot() + 
  geom_sf(data = nearctic_biomes, aes(geometry = geometry)) + 
  geom_sf(data = nearctic_cities, aes(geometry = geometry, color = cluster))

ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'neartic_clusters.jpg'))
Saving 7.29 x 4.51 in image

Neartic Cluster 1

nearctic_cities %>% filter(cluster == 1) %>% plot_city_cluster('Neartic cluster 1')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'neartic_cluster1.jpg'))
Saving 7.29 x 4.51 in image

Neartic Cluster 2

nearctic_cities %>% filter(cluster == 2) %>% plot_city_cluster('Neartic cluster 2')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'neartic_cluster2.jpg'))
Saving 7.29 x 4.51 in image

Neartic Cluster 3

nearctic_cities %>% filter(cluster == 3) %>% plot_city_cluster('Neartic cluster 3')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'neartic_cluster3.jpg'))
Saving 7.29 x 4.51 in image

Neartic Cluster 4

nearctic_cities %>% filter(cluster == 4) %>% plot_city_cluster('Neartic cluster 4')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'neartic_cluster4.jpg'))
Saving 7.29 x 4.51 in image

Neartic Cluster 5

nearctic_cities %>% filter(cluster == 5) %>% plot_city_cluster('Neartic cluster 5')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'neartic_cluster5.jpg'))
Saving 7.29 x 4.51 in image

Neotropic

neotropic_cities_community_data = community_data_metrics %>% filter(core_realm == 'Neotropic')
neotropic_cities_community_data %>% dplyr::select(city_name) %>% distinct() %>% as.list()
$city_name
 [1] "Culiacán"                  "Guadalajara"               "Morelia"                   "Acapulco"                  "Querétaro"                
 [6] "Cuernavaca"                "Puebla"                    "Oaxaca"                    "Xalapa"                    "Veracruz"                 
[11] "Tuxtla Gutiérrez"          "Villahermosa"              "Guatemala City"            "San Salvador"              "San Pedro Sula"           
[16] "Mérida"                    "Tegucigalpa"               "Managua"                   "San José"                  "Cancún"                   
[21] "Guayaquil"                 "Chiclayo"                  "Panama City"               "Trujillo"                  "Quito"                    
[26] "Havana"                    "Cali"                      "Lima"                      "Pereira"                   "Miami"                    
[31] "Medellín"                  "Ibagué"                    "Cartagena"                 "Kingston"                  "Bogota"                   
[36] "Barranquilla"              "Bucaramanga"               "Cúcuta"                    "Maracaibo"                 "Arequipa"                 
[41] "Barquisimeto"              "Santo Domingo"             "Maracay"                   "El Alto [La Paz]"          "Caracas"                  
[46] "Cochabamba"                "Viña del Mar [Valparaíso]" "Río Piedras [San Juan]"    "Barcelona"                 "Concepción"               
[51] "Santiago"                  "Mendoza"                   "Salta"                     "Cordoba"                   "Asuncion"                 
[56] "Buenos Aires"              "La Plata"                  "Ciudad del Este"           "Montevideo"                "Mar del Plata"            
[61] "Porto Alegre"              "São Paulo"                 "Santos"                    "Sao Jose dos Campos"      
neotropic_cities_nmds = community_nmds(communities %>% filter(city_id %in% neotropic_cities_community_data$city_id)) 
Run 0 stress 0.134619 
Run 1 stress 0.141222 
Run 2 stress 0.1348238 
... Procrustes: rmse 0.01072062  max resid 0.05476751 
Run 3 stress 0.1348237 
... Procrustes: rmse 0.01071747  max resid 0.05475478 
Run 4 stress 0.134433 
... New best solution
... Procrustes: rmse 0.006824883  max resid 0.04575085 
Run 5 stress 0.134619 
... Procrustes: rmse 0.006831176  max resid 0.04573137 
Run 6 stress 0.1405634 
Run 7 stress 0.1348046 
... Procrustes: rmse 0.006552356  max resid 0.04609573 
Run 8 stress 0.141222 
Run 9 stress 0.1346226 
... Procrustes: rmse 0.007200092  max resid 0.04572692 
Run 10 stress 0.1414222 
Run 11 stress 0.1363758 
Run 12 stress 0.1363675 
Run 13 stress 0.1344331 
... Procrustes: rmse 0.00002617473  max resid 0.00009028585 
... Similar to previous best
Run 14 stress 0.1344509 
... Procrustes: rmse 0.00118731  max resid 0.007067449 
... Similar to previous best
Run 15 stress 0.134619 
... Procrustes: rmse 0.006829458  max resid 0.04574559 
Run 16 stress 0.134433 
... Procrustes: rmse 0.00002418546  max resid 0.00008384028 
... Similar to previous best
Run 17 stress 0.134433 
... New best solution
... Procrustes: rmse 0.000008862921  max resid 0.00003722846 
... Similar to previous best
Run 18 stress 0.141222 
Run 19 stress 0.134433 
... Procrustes: rmse 0.00003797808  max resid 0.0001268688 
... Similar to previous best
Run 20 stress 0.1348046 
... Procrustes: rmse 0.006553689  max resid 0.04610243 
*** Solution reached
neotropic_cities_nmds
scree_plot(neotropic_cities_nmds)

neotropic_cities = cluster_cities(city_nmds = neotropic_cities_nmds, cities_community_data = neotropic_cities_community_data, centers = 5)
Joining with `by = join_by(city_id)`
plot_nmds_clusters(neotropic_cities)

neotropic_biomes = resolve[resolve$REALM == 'Neotropic',c('REALM')]
 
ggplot() + 
  geom_sf(data = neotropic_biomes, aes(geometry = geometry)) + 
  geom_sf(data = neotropic_cities, aes(geometry = geometry, color = cluster))
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'neotropic_clusters.jpg'))
Saving 7.29 x 4.51 in image

Neotropic Cluster 1

neotropic_cities %>% filter(cluster == 1) %>% plot_city_cluster('Neotropic cluster 1')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'neotropic_cluster1.jpg'))
Saving 7.29 x 4.51 in image

Neotropic Cluster 2

neotropic_cities %>% filter(cluster == 2) %>% plot_city_cluster('Neotropic cluster 2')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'neotropic_cluster2.jpg'))
Saving 7.29 x 4.51 in image

Neotropic Cluster 3

neotropic_cities %>% filter(cluster == 3) %>% plot_city_cluster('Neotropic cluster 3')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'neotropic_cluster3.jpg'))
Saving 7.29 x 4.51 in image

Neotropic Cluster 4

neotropic_cities %>% filter(cluster == 4) %>% plot_city_cluster('Neotropic cluster 4')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'neotropic_cluster4.jpg'))
Saving 7.29 x 4.51 in image

Neotropic Cluster 5

neotropic_cities %>% filter(cluster == 5) %>% plot_city_cluster('Neotropic cluster 5')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'neotropic_cluster5.jpg'))
Saving 7.29 x 4.51 in image

Palearctic

palearctic_cities_community_data = community_data_metrics %>% filter(core_realm == 'Palearctic')
palearctic_cities_community_data %>% dplyr::select(city_name) %>% distinct() %>% as.list()
$city_name
 [1] "Lisbon"                "Porto"                 "Marrakesh"             "Seville"               "Dublin"                "Málaga"               
 [7] "Madrid"                "Glasgow"               "Bilbao"                "Liverpool"             "Bristol"               "Manchester"           
[13] "Birmingham"            "Leeds"                 "Newcastle upon Tyne"   "Sheffield"             "Nottingham"            "Valencia"             
[19] "London"                "Toulouse"              "Paris"                 "Barcelona"             "Rotterdam [The Hague]" "Brussels"             
[25] "Amsterdam"             "Lyon"                  "Marseille"             "Dusseldorf"            "Nice"                  "Frankfurt am Main"    
[31] "Zurich"                "Oslo"                  "Stuttgart"             "Hamburg"               "Genoa"                 "Nuremberg"            
[37] "Copenhagen"            "Munich"                "Berlin"                "Dresden"               "Rome"                  "Prague"               
[43] "Stockholm"             "Poznan"                "Vienna"                "Wroclaw"               "Zagreb"                "Gdansk"               
[49] "Budapest"              "Krakow"                "Warsaw"                "Helsinki"              "Riga"                  "Belgrade"             
[55] "Lviv"                  "Sofia"                 "Thessaloniki"          "Minsk"                 "Athens"                "Kyiv"                 
[61] "Istanbul"              "Odesa"                 "Samsun"                "Luxor"                 "Tel Aviv"              "Jerusalem"            
[67] "Tbilisi"               "Yerevan"               "Abu Dhabi"             "Dubai"                 "Bishkek"              
palearctic_cities_nmds = community_nmds(communities %>% filter(city_id %in% palearctic_cities_community_data$city_id)) 
Run 0 stress 0.04203818 
Run 1 stress 0.05064867 
Run 2 stress 0.07206189 
Run 3 stress 0.04240445 
... Procrustes: rmse 0.03948386  max resid 0.2064496 
Run 4 stress 0.1164521 
Run 5 stress 0.05197845 
Run 6 stress 0.1332585 
Run 7 stress 0.05064862 
Run 8 stress 0.08350482 
Run 9 stress 0.04984194 
Run 10 stress 0.065365 
Run 11 stress 0.07340577 
Run 12 stress 0.04203776 
... New best solution
... Procrustes: rmse 0.00009496175  max resid 0.0003265871 
... Similar to previous best
Run 13 stress 0.05819636 
Run 14 stress 0.05530089 
Run 15 stress 0.1107012 
Run 16 stress 0.04223506 
... Procrustes: rmse 0.04136181  max resid 0.2169081 
Run 17 stress 0.1026199 
Run 18 stress 0.04223585 
... Procrustes: rmse 0.04135834  max resid 0.2172361 
Run 19 stress 0.08634193 
Run 20 stress 0.07085973 
*** Solution reached
palearctic_cities_nmds
scree_plot(palearctic_cities_nmds)
Warning: did not converge in 10 iterationsWarning: did not converge in 10 iterationsWarning: did not converge in 10 iterationsWarning: did not converge in 10 iterationsWarning: Quick-TRANSfer stage steps exceeded maximum (= 3550)Warning: did not converge in 10 iterationsWarning: did not converge in 10 iterationsWarning: Quick-TRANSfer stage steps exceeded maximum (= 3550)

palearctic_cities = cluster_cities(city_nmds = palearctic_cities_nmds, cities_community_data = palearctic_cities_community_data, centers = 6)
Joining with `by = join_by(city_id)`
plot_nmds_clusters(palearctic_cities)

palearctic_biomes = resolve[resolve$REALM == 'Palearctic',c('REALM')]
 
ggplot() + 
  geom_sf(data = palearctic_biomes, aes(geometry = geometry)) + 
  geom_sf(data = palearctic_cities, aes(geometry = geometry, color = cluster))
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'palearctic_clusters.jpg'))
Saving 7.29 x 4.51 in image

Palearctic Cluster 1

palearctic_cities %>% filter(cluster == 1) %>% plot_city_cluster('Palearctic cluster 1')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'palearctic_cluster1.jpg'))
Saving 7.29 x 4.51 in image

Palearctic Cluster 2

palearctic_cities %>% filter(cluster == 2) %>% plot_city_cluster('Palearctic cluster 2')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'palearctic_cluster2.jpg'))
Saving 7.29 x 4.51 in image

Palearctic Cluster 3

palearctic_cities %>% filter(cluster == 3) %>% plot_city_cluster('Palearctic cluster 3')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.

ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'palearctic_cluster3.jpg'))
Saving 14 x 6 in image

Palearctic Cluster 4

palearctic_cities %>% filter(cluster == 4) %>% plot_city_cluster('Palearctic cluster 4')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'palearctic_cluster4.jpg'))
Saving 7.29 x 4.51 in image

Palearctic Cluster 5

palearctic_cities %>% filter(cluster == 5) %>% plot_city_cluster('Palearctic cluster 5')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'palearctic_cluster5.jpg'))
Saving 7.29 x 4.51 in image

Palearctic Cluster 6

palearctic_cities %>% filter(cluster == 6) %>% plot_city_cluster('Palearctic cluster 6')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'palearctic_cluster6.jpg'))
Saving 7.29 x 4.51 in image

Afrotropic

afrotropic_cities_community_data = community_data_metrics %>% filter(core_realm == 'Afrotropic')
afrotropic_cities_community_data %>% dplyr::select(city_name) %>% distinct() %>% as.list()
$city_name
[1] "Cape Town"    "Johannesburg" "Pretoria"     "Kigali"       "Kampala"      "Arusha"       "Nairobi"      "Addis Ababa" 
afrotropic_cities_nmds = community_nmds(communities %>% filter(city_id %in% afrotropic_cities_community_data$city_id)) 
Run 0 stress 0.06095916 
Run 1 stress 0.06095916 
... New best solution
... Procrustes: rmse 0.00001059092  max resid 0.00001521966 
... Similar to previous best
Run 2 stress 0.1254289 
Run 3 stress 0.126785 
Run 4 stress 0.126785 
Run 5 stress 0.06095935 
... Procrustes: rmse 0.0001624829  max resid 0.0003126969 
... Similar to previous best
Run 6 stress 0.3007162 
Run 7 stress 0.126785 
Run 8 stress 0.126785 
Run 9 stress 0.06095923 
... Procrustes: rmse 0.0006753449  max resid 0.001228519 
... Similar to previous best
Run 10 stress 0.06095916 
... Procrustes: rmse 0.000006448791  max resid 0.00001242034 
... Similar to previous best
Run 11 stress 0.06095923 
... Procrustes: rmse 0.00004902177  max resid 0.00008420442 
... Similar to previous best
Run 12 stress 0.1246142 
Run 13 stress 0.126785 
Run 14 stress 0.126785 
Run 15 stress 0.1973021 
Run 16 stress 0.1246142 
Run 17 stress 0.06095904 
... New best solution
... Procrustes: rmse 0.0002457047  max resid 0.0004426671 
... Similar to previous best
Run 18 stress 0.0609592 
... Procrustes: rmse 0.0002974389  max resid 0.0005449775 
... Similar to previous best
Run 19 stress 0.1354466 
Run 20 stress 0.126785 
*** Solution reached
afrotropic_cities_nmds
scree_plot(afrotropic_cities_nmds)

afrotropic_cities = cluster_cities(city_nmds = afrotropic_cities_nmds, cities_community_data = afrotropic_cities_community_data, centers = 4)
Joining with `by = join_by(city_id)`
plot_nmds_clusters(afrotropic_cities)

afrotropic_biomes = resolve[resolve$REALM == 'Afrotropic',c('REALM')]
 
ggplot() + 
  geom_sf(data = afrotropic_biomes, aes(geometry = geometry)) + 
  geom_sf(data = afrotropic_cities, aes(geometry = geometry, color = cluster))
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'afrotropic_clusters.jpg'))
Saving 7.29 x 4.51 in image

Afrotropic Cluster 1

afrotropic_cities %>% filter(cluster == 1) %>% plot_city_cluster('Afrotropic cluster 1')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'afrotropic_cluster1.jpg'))
Saving 7.29 x 4.51 in image

Afrotropic Cluster 2

afrotropic_cities %>% filter(cluster == 2) %>% plot_city_cluster('Afrotropic cluster 2')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'afrotropic_cluster2.jpg'))
Saving 7.29 x 4.51 in image

Afrotropic Cluster 3

afrotropic_cities %>% filter(cluster == 3) %>% plot_city_cluster('Afrotropic cluster 3')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'afrotropic_cluster3.jpg'))
Saving 7.29 x 4.51 in image

Afrotropic Cluster 4

afrotropic_cities %>% filter(cluster == 4) %>% plot_city_cluster('Afrotropic cluster 4')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'afrotropic_cluster4.jpg'))
Saving 7.29 x 4.51 in image

Indomalayan

indomalayan_cities_community_data = community_data_metrics %>% filter(core_realm == 'Indomalayan')
indomalayan_cities_community_data %>% dplyr::select(city_name) %>% distinct() %>% as.list()
$city_name
  [1] "Swat City"           "Srinagar"            "Jamnagar"            "Jammu"               "Rajkot"              "Bikaner"             "Jodhpur"            
  [8] "Jalandhar"           "Ahmedabad"           "Bhavnagar"           "Ludhiana"            "Anand"               "Udaipur"             "Surat"              
 [15] "Vadodara"            "Ajmer"               "Chandigarh"          "Vasai-Virar"         "Mumbai"              "Jaipur"              "Delhi [New Delhi]"  
 [22] "Nashik"              "Dehradun"            "Kota"                "Pune"                "Haridwar"            "Dhule"               "Ujjain"             
 [29] "Indore"              "Ahmadnagar"          "Kolhapur"            "Jalgaon"             "Agra"                "Aurangabad"          "Sangli"             
 [36] "Belagavi"            "Gwalior"             "Budaun"              "Bareilly"            "Dharwad"             "Bhopal"              "Bhind"              
 [43] "Mangaluru"           "Solapur"             "Vijayapura"          "Akola"               "Latur"               "Kannur"              "Davanagere"         
 [50] "Thalassery"          "Amravati"            "Kalaburagi"          "Kozhikode"           "Guruvayur"           "Malappuram"          "Lucknow"            
 [57] "Thrissur"            "Mysuru"              "Kochi"               "Nagpur"              "Kollam"              "Jabalpur"            "Ettumanoor"         
 [64] "Hyderabad"           "Coimbatore"          "Bengaluru"           "Thiruvananthapuram"  "Tiruppur"            "Faizabad"            "Erode"              
 [71] "Prayagraj"           "Pratapgarh"          "Salem"               "Dindigul"            "Madurai"             "Tiruchirappalli"     "Durg"               
 [78] "Vellore"             "Tirupati"            "Raipur"              "Bilaspur"            "Vijayawada"          "Puducherry"          "Chennai"            
 [85] "Kathmandu"           "Colombo"             "Rajamahendravaram"   "Patna"               "Kandy"               "Bihar Sharif"        "Visakhapatnam"      
 [92] "Ranchi"              "Brahmapur"           "Jamshedpur"          "Darjeeling"          "Siliguri"            "Cuttack"             "Bhubaneshwar"       
 [99] "Jalpaiguri"          "Berhampore"          "Kolkata"             "Krishnanagar"        "Guwahati [Dispur]"   "Agartala"            "Silchar"            
[106] "Dimapur"             "Bangkok"             "George Town"         "Kuala Lumpur"        "Phnom Penh"          "Singapore"           "Hong Kong"          
[113] "Sha Tin"             "Hsinchu"             "Taichung"            "New Taipei [Taipei]" "Tainan"              "Denpasar"            "Kaohsiung"          
[120] "Kota Kinabalu"      
indomalayan_cities_nmds = community_nmds(communities %>% filter(city_id %in% indomalayan_cities_community_data$city_id)) 
Run 0 stress 0.1199339 
Run 1 stress 0.1221765 
Run 2 stress 0.1574272 
Run 3 stress 0.165272 
Run 4 stress 0.1180044 
... New best solution
... Procrustes: rmse 0.03780687  max resid 0.2489361 
Run 5 stress 0.1223128 
Run 6 stress 0.128065 
Run 7 stress 0.1462967 
Run 8 stress 0.1177488 
... New best solution
... Procrustes: rmse 0.01878365  max resid 0.1301728 
Run 9 stress 0.1480994 
Run 10 stress 0.1205201 
Run 11 stress 0.1380584 
Run 12 stress 0.1377785 
Run 13 stress 0.1241019 
Run 14 stress 0.1174802 
... New best solution
... Procrustes: rmse 0.003927371  max resid 0.03013146 
Run 15 stress 0.1617252 
Run 16 stress 0.1645131 
Run 17 stress 0.1603814 
Run 18 stress 0.1241049 
Run 19 stress 0.1596718 
Run 20 stress 0.1228515 
Run 21 stress 0.1597143 
Run 22 stress 0.1547365 
Run 23 stress 0.1488411 
Run 24 stress 0.1464657 
Run 25 stress 0.1441819 
Run 26 stress 0.1221274 
Run 27 stress 0.1195283 
Run 28 stress 0.115795 
... New best solution
... Procrustes: rmse 0.02711525  max resid 0.2451229 
Run 29 stress 0.1224208 
Run 30 stress 0.1167315 
*** No convergence -- monoMDS stopping criteria:
     2: no. of iterations >= maxit
    27: stress ratio > sratmax
     1: scale factor of the gradient < sfgrmin
indomalayan_cities_nmds
scree_plot(indomalayan_cities_nmds)

indomalayan_cities = cluster_cities(city_nmds = indomalayan_cities_nmds, cities_community_data = indomalayan_cities_community_data, centers = 6)
Joining with `by = join_by(city_id)`
plot_nmds_clusters(indomalayan_cities)

indomalayan_biomes = resolve[resolve$REALM == 'Indomalayan',c('REALM')]
 
ggplot() + 
  geom_sf(data = indomalayan_biomes, aes(geometry = geometry)) + 
  geom_sf(data = indomalayan_cities, aes(geometry = geometry, color = cluster))
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'indomalayan_clusters.jpg'))
Saving 7.29 x 4.51 in image

Indomalayan Cluster 1

indomalayan_cities %>% filter(cluster == 1) %>% plot_city_cluster('Indomalayan cluster 1')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'indomalayan_cluster1.jpg'))
Saving 7.29 x 4.51 in image

Indomalayan Cluster 2

indomalayan_cities %>% filter(cluster == 2) %>% plot_city_cluster('Indomalayan cluster 2')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'indomalayan_cluster2.jpg'))
Saving 7.29 x 4.51 in image

Indomalayan Cluster 3

indomalayan_cities %>% filter(cluster == 3) %>% plot_city_cluster('Indomalayan cluster 3')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'indomalayan_cluster3.jpg'))
Saving 7.29 x 4.51 in image

Indomalayan Cluster 4

indomalayan_cities %>% filter(cluster == 4) %>% plot_city_cluster('Indomalayan cluster 4')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'indomalayan_cluster4.jpg'))
Saving 7.29 x 4.51 in image

Indomalayan Cluster 5

indomalayan_cities %>% filter(cluster == 5) %>% plot_city_cluster('Indomalayan cluster 5')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'indomalayan_cluster5.jpg'))
Saving 7.29 x 4.51 in image

Indomalayan Cluster 6

indomalayan_cities %>% filter(cluster == 6) %>% plot_city_cluster('Indomalayan cluster 6')
`summarise()` has grouped output by 'jetz_species_name', 'city_name', 'present_urban_high', 'present_urban_med'. You can override using the `.groups` argument.
ggsave(filename(REGION_DEEP_DIVE_FIGURES_OUTPUT, 'indomalayan_cluster6.jpg'))
Saving 7.29 x 4.51 in image

LS0tCnRpdGxlOiAiRGVlcCBkaXZlIHJlZ2lvbmFsIGNvbW11bml0aWVzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKYmlibGlvZ3JhcGh5OiAuLi9yZWYuYmliICAKLS0tCgpgYGB7cn0Kc291cmNlKCcuLi9lbnYuUicpCmBgYAoKIyBTcGVjaWVzIGluIGNvbW11bml0aWVzCkl0IHNlZW1zIHJlYXNvbmFibGUgdG8gZXhwZWN0IHRoYXQgY2l0aWVzIHdpdGggc2ltaWFsciByZWdpb25hbCBwb29scyB3aWxsIGhhdmUgc2ltaWxhciBzcGVjaWVzIGVudGVyaW5nIHRoZSBjaXR5LCBhbmQgdGh1cyBhIHNpbWlsYXIgcmVzcG9uc2UgdG8gdXJiYW5pc2F0aW9uLgoKIyMgTG9hZCBkYXRhCmBgYHtyfQpjb21tdW5pdGllcyA9IHJlYWRfY3N2KGZpbGVuYW1lKENPTU1VTklUWV9PVVRQVVRfRElSLCAnY29tbXVuaXRpZXNfZm9yX2FuYWx5c2lzLmNzdicpKQoKY29tbXVuaXRpZXNfc3VtbWFyeSA9IGNvbW11bml0aWVzICU+JSBncm91cF9ieShjaXR5X2lkKSAlPiUgc3VtbWFyaXNlKAogIHJlZ2lvbmFsX3Bvb2xfc2l6ZSA9IG4oKSwgCiAgdXJiYW5fc2l6ZV9oaWdoID0gc3VtKHByZXNlbnRfdXJiYW5faGlnaCksIAogIHVyYmFuX3NpemVfbWVkID0gc3VtKHByZXNlbnRfdXJiYW5fbWVkKSwgCiAgdXJiYW5fc2l6ZV9sb3cgPSBzdW0ocHJlc2VudF91cmJhbl9sb3cpCikKCmNvbW11bml0aWVzX3N1bW1hcnlfbG9uZyA9IGJpbmRfcm93cygKICBjb21tdW5pdGllc19zdW1tYXJ5ICU+JSByZW5hbWUodXJiYW5fcG9vbF9zaXplID0gJ3VyYmFuX3NpemVfaGlnaCcpICU+JSBkcGx5cjo6c2VsZWN0KGNpdHlfaWQsIHJlZ2lvbmFsX3Bvb2xfc2l6ZSwgdXJiYW5fcG9vbF9zaXplKSAlPiUgbXV0YXRlKGlzX3VyYmFuX3RocmVzaG9sZCA9ICdIaWdoJyksCiAgY29tbXVuaXRpZXNfc3VtbWFyeSAlPiUgcmVuYW1lKHVyYmFuX3Bvb2xfc2l6ZSA9ICd1cmJhbl9zaXplX21lZCcpICU+JSBkcGx5cjo6c2VsZWN0KGNpdHlfaWQsIHJlZ2lvbmFsX3Bvb2xfc2l6ZSwgdXJiYW5fcG9vbF9zaXplKSAlPiUgbXV0YXRlKGlzX3VyYmFuX3RocmVzaG9sZCA9ICdNZWRpdW0nKSwKICBjb21tdW5pdGllc19zdW1tYXJ5ICU+JSByZW5hbWUodXJiYW5fcG9vbF9zaXplID0gJ3VyYmFuX3NpemVfbG93JykgJT4lIGRwbHlyOjpzZWxlY3QoY2l0eV9pZCwgcmVnaW9uYWxfcG9vbF9zaXplLCB1cmJhbl9wb29sX3NpemUpICU+JSBtdXRhdGUoaXNfdXJiYW5fdGhyZXNob2xkID0gJ0xvdycpCikKY29tbXVuaXRpZXNfc3VtbWFyeV9sb25nCmBgYApgYGB7cn0KY2l0eV9wb2ludHMgPSBzdF9jZW50cm9pZChyZWFkX3NmKGZpbGVuYW1lKENJVFlfREFUQV9PVVRQVVRfRElSLCAnY2l0eV9zZWxlY3Rpb24uc2hwJykpKQpgYGAKCmBgYHtyfQpjb21tdW5pdHlfZGF0YV9tZXRyaWNzID0gcmVhZF9jc3YoZmlsZW5hbWUoQ09NTVVOSVRZX09VVFBVVF9ESVIsICdjb21tdW5pdHlfYXNzZW1ibHlfbWV0cmljcy5jc3YnKSkgJT4lCiAgZHBseXI6OnNlbGVjdChjaXR5X2lkLCBtbnRkX25vcm1hbGlzZWQsIGZkX25vcm1hbGlzZWQsIHVyYmFuX3Bvb2xfc2l6ZSwgaXNfdXJiYW5fdGhyZXNob2xkKSAlPiUKICBsZWZ0X2pvaW4ocmVhZF9jc3YoZmlsZW5hbWUoQ0lUWV9EQVRBX09VVFBVVF9ESVIsICdyZWFsbXMuY3N2JykpKSAlPiUKICBsZWZ0X2pvaW4oY29tbXVuaXRpZXNfc3VtbWFyeV9sb25nKSAlPiUKICBsZWZ0X2pvaW4oY2l0eV9wb2ludHNbLGMoJ2NpdHlfaWQnLCAnY2l0eV9ubScpXSkgJT4lCiAgcmVuYW1lKGNpdHlfbmFtZT0nY2l0eV9ubScpICU+JQogIG11dGF0ZShpc191cmJhbl90aHJlc2hvbGQgPSBmYWN0b3IoaXNfdXJiYW5fdGhyZXNob2xkLCBsZXZlbHMgPSBjKCdsb3cnLCAnbWVkaXVtJywgJ2hpZ2gnKSwgbGFiZWxzID0gYygnTG93JywgJ01lZGl1bScsICdIaWdoJyksIG9yZGVyZWQgPSBUKSkgJT4lCiAgYXJyYW5nZShjaXR5X2lkKQoKY29tbXVuaXR5X2RhdGFfbWV0cmljcwpgYGAKCkxvYWQgdHJhaXQgZGF0YQpgYGB7cn0KdHJhaXRzID0gcmVhZF9jc3YoZmlsZW5hbWUoVEFYT05PTVlfT1VUUFVUX0RJUiwgJ3RyYWl0c19qZXR6LmNzdicpKQpoZWFkKHRyYWl0cykKYGBgCmBgYHtyfQpmZXRjaF9ub3JtYWxpc2VkX3RyYWl0cyA9IGZ1bmN0aW9uKHJlcXVpcmVkX3NwZWNpZXNfbGlzdCkgewogIHJlcXVpcmVkX3RyYWl0cyA9IHRyYWl0cyAlPiUgZmlsdGVyKGpldHpfc3BlY2llc19uYW1lICVpbiUgcmVxdWlyZWRfc3BlY2llc19saXN0KQogIAogIHJlcXVpcmVkX3RyYWl0cyRnYXBlX3dpZHRoX25vcm1hbGlzZWQgPSBub3JtYWxpc2UocmVxdWlyZWRfdHJhaXRzJGdhcGVfd2lkdGgsIG1pbihyZXF1aXJlZF90cmFpdHMkZ2FwZV93aWR0aCksIG1heChyZXF1aXJlZF90cmFpdHMkZ2FwZV93aWR0aCkpCiAgcmVxdWlyZWRfdHJhaXRzJHRyb3BoaWNfdHJhaXRfbm9ybWFsaXNlZCA9IG5vcm1hbGlzZShyZXF1aXJlZF90cmFpdHMkdHJvcGhpY190cmFpdCwgbWluKHJlcXVpcmVkX3RyYWl0cyR0cm9waGljX3RyYWl0KSwgbWF4KHJlcXVpcmVkX3RyYWl0cyR0cm9waGljX3RyYWl0KSkKICByZXF1aXJlZF90cmFpdHMkbG9jb21vdG9yeV90cmFpdF9ub3JtYWxpc2VkID0gbm9ybWFsaXNlKHJlcXVpcmVkX3RyYWl0cyRsb2NvbW90b3J5X3RyYWl0LCBtaW4ocmVxdWlyZWRfdHJhaXRzJGxvY29tb3RvcnlfdHJhaXQpLCBtYXgocmVxdWlyZWRfdHJhaXRzJGxvY29tb3RvcnlfdHJhaXQpKQogIHJlcXVpcmVkX3RyYWl0cyRtYXNzX25vcm1hbGlzZWQgPSBub3JtYWxpc2UocmVxdWlyZWRfdHJhaXRzJG1hc3MsIG1pbihyZXF1aXJlZF90cmFpdHMkbWFzcyksIG1heChyZXF1aXJlZF90cmFpdHMkbWFzcykpCiAgCiAgdHJhaXRzX25vcm1hbGlzZWRfbG9uZyA9IHJlcXVpcmVkX3RyYWl0cyAlPiUgcGl2b3RfbG9uZ2VyKGNvbHMgPSBjKCdnYXBlX3dpZHRoX25vcm1hbGlzZWQnLCAndHJvcGhpY190cmFpdF9ub3JtYWxpc2VkJywgJ2xvY29tb3RvcnlfdHJhaXRfbm9ybWFsaXNlZCcsICdtYXNzX25vcm1hbGlzZWQnKSwgbmFtZXNfdG8gPSAndHJhaXQnLCB2YWx1ZXNfdG8gPSAnbm9ybWFsaXNlZF92YWx1ZScpICU+JSBkcGx5cjo6c2VsZWN0KGpldHpfc3BlY2llc19uYW1lLCB0cmFpdCwgbm9ybWFsaXNlZF92YWx1ZSkKICB0cmFpdHNfbm9ybWFsaXNlZF9sb25nJHRyYWl0ID0gZmFjdG9yKHRyYWl0c19ub3JtYWxpc2VkX2xvbmckdHJhaXQsIGxldmVscyA9IGMoJ2dhcGVfd2lkdGhfbm9ybWFsaXNlZCcsICd0cm9waGljX3RyYWl0X25vcm1hbGlzZWQnLCAnbG9jb21vdG9yeV90cmFpdF9ub3JtYWxpc2VkJywgJ21hc3Nfbm9ybWFsaXNlZCcpLCBsYWJlbHMgPSBjKCdHYXBlIFdpZHRoJywgJ1Ryb3BoaWMgVHJhaXQnLCAnTG9jb21vdG9yeSBUcmFpdCcsICdNYXNzJykpCiAgCiAgdHJhaXRzX25vcm1hbGlzZWRfbG9uZwp9CgpmZXRjaF9ub3JtYWxpc2VkX3RyYWl0cyhjKCdBcGxvcGVsaWFfbGFydmF0YScsICdDaGFsY29waGFwc19pbmRpY2EnLCAnQ2Fsb2VuYXNfbmljb2JhcmljYScpKQpgYGAKCgpSZWFkIGluIG91ciBwaHlsb2dlbnkKYGBge3J9CnBoeWxvX3RyZWUgPSByZWFkLnRyZWUoZmlsZW5hbWUoVEFYT05PTVlfT1VUUFVUX0RJUiwgJ3BoeWxvZ2VueS50cmUnKSkKZ2d0cmVlKHBoeWxvX3RyZWUsIGxheW91dD0nY2lyY3VsYXInKQpgYGAKCkxvYWQgcmVzb2x2ZSBlY29yZWdpb25zCmBgYHtyfQpyZXNvbHZlID0gcmVhZF9yZXNvbHZlKCkKYGBgCgojIyBDcmVhdGUgaGVscGVyIGZ1bmN0aW9ucwpgYGB7cn0KdG9fc3BlY2llc19tYXRyaXggPSBmdW5jdGlvbihmaWx0ZXJlZF9jb21tdW5pdGllcykgewogIGZpbHRlcmVkX2NvbW11bml0aWVzICU+JSAKICAgIGRwbHlyOjpzZWxlY3QoY2l0eV9pZCwgamV0el9zcGVjaWVzX25hbWUpICU+JSAKICAgIGRpc3RpbmN0KCkgJT4lCiAgICBtdXRhdGUocHJlc2VudCA9IFRSVUUpICU+JSAKICAgIHBpdm90X3dpZGVyKAogICAgICBuYW1lc19mcm9tID0gamV0el9zcGVjaWVzX25hbWUsIAogICAgICB2YWx1ZXNfZnJvbSA9ICJwcmVzZW50IiwgCiAgICAgIHZhbHVlc19maWxsID0gbGlzdChwcmVzZW50ID0gRikKICAgICkgJT4lIAogICAgdGliYmxlOjpjb2x1bW5fdG9fcm93bmFtZXModmFyPSdjaXR5X2lkJykKfQpgYGAKCmBgYHtyfQpjb21tdW5pdHlfbm1kcyA9IGZ1bmN0aW9uKGZpbHRlcmVkX2NvbW11bml0aWVzKSB7CiAgc3BlY2llc19tYXRyaXggPSB0b19zcGVjaWVzX21hdHJpeChmaWx0ZXJlZF9jb21tdW5pdGllcykKICBubWRzIDwtIG1ldGFNRFMoc3BlY2llc19tYXRyaXgsIGs9MiwgdHJ5bWF4ID0gMzApCiAgbm1kc19yZXN1bHQgPSBkYXRhLmZyYW1lKHNjb3JlcyhubWRzKSkKICBubWRzX3Jlc3VsdCRjaXR5X2lkID0gYXMuZG91YmxlKHJvd25hbWVzKHNjb3JlcyhubWRzKSkpCiAgcm93bmFtZXMobm1kc19yZXN1bHQpID0gTlVMTAogIAogIG5tZHNfcmVzdWx0Cn0KYGBgCgpodHRwczovL3d3dy5kYXRhY2FtcC5jb20vdHV0b3JpYWwvay1tZWFucy1jbHVzdGVyaW5nLXIKYGBge3J9CnNjcmVlX3Bsb3QgPSBmdW5jdGlvbihjb21tdW5pdHlfbm1kc19kYXRhKSB7CiAgIyBEZWNpZGUgaG93IG1hbnkgY2x1c3RlcnMgdG8gbG9vayBhdAogIG5fY2x1c3RlcnMgPC0gbWluKDEwLCBucm93KGNvbW11bml0eV9ubWRzX2RhdGEpIC0gMSkKICAKICAjIEluaXRpYWxpemUgdG90YWwgd2l0aGluIHN1bSBvZiBzcXVhcmVzIGVycm9yOiB3c3MKICB3c3MgPC0gbnVtZXJpYyhuX2NsdXN0ZXJzKQogIAogIHNldC5zZWVkKDEyMykKICAKICAjIExvb2sgb3ZlciAxIHRvIG4gcG9zc2libGUgY2x1c3RlcnMKICBmb3IgKGkgaW4gMTpuX2NsdXN0ZXJzKSB7CiAgICAjIEZpdCB0aGUgbW9kZWw6IGttLm91dAogICAga20ub3V0IDwtIGttZWFucyhjb21tdW5pdHlfbm1kc19kYXRhWyxjKCdOTURTMScsJ05NRFMyJyldLCBjZW50ZXJzID0gaSwgbnN0YXJ0ID0gMjApCiAgICAjIFNhdmUgdGhlIHdpdGhpbiBjbHVzdGVyIHN1bSBvZiBzcXVhcmVzCiAgICB3c3NbaV0gPC0ga20ub3V0JHRvdC53aXRoaW5zcwogIH0KICAKICAjIFByb2R1Y2UgYSBzY3JlZSBwbG90CiAgd3NzX2RmIDwtIHRpYmJsZShjbHVzdGVycyA9IDE6bl9jbHVzdGVycywgd3NzID0gd3NzKQogICAKICBzY3JlZV9wbG90IDwtIGdncGxvdCh3c3NfZGYsIGFlcyh4ID0gY2x1c3RlcnMsIHkgPSB3c3MsIGdyb3VwID0gMSkpICsKICAgICAgZ2VvbV9wb2ludChzaXplID0gNCkgKwogICAgICBnZW9tX2xpbmUoKSArCiAgICAgIGdlb21faGxpbmUobGluZXR5cGU9ImRhc2hlZCIsIGNvbG9yID0gIm9yYW5nZSIsIHlpbnRlcmNlcHQgPSB3c3MpICsKICAgICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IGMoMiwgNCwgNiwgOCwgMTApKSArCiAgICAgIHhsYWIoJ051bWJlciBvZiBjbHVzdGVycycpCiAgc2NyZWVfcGxvdAp9CmBgYAoKYGBge3J9CmNsdXN0ZXJfY2l0aWVzID0gZnVuY3Rpb24oY2l0eV9ubWRzLCBjaXRpZXNfY29tbXVuaXR5X2RhdGEsIGNlbnRlcnMpIHsKICBzZXQuc2VlZCgxMjMpCiAga21lYW5zX2NsdXN0ZXJzIDwtIGttZWFucyhjaXR5X25tZHNbLGMoJ05NRFMxJywgJ05NRFMyJyldLCBjZW50ZXJzID0gY2VudGVycywgbnN0YXJ0ID0gMjApCiAgY2l0eV9ubWRzJGNsdXN0ZXIgPSBrbWVhbnNfY2x1c3RlcnMkY2x1c3RlcgogIGNpdGllc19jb21tdW5pdHlfZGF0YSAlPiUgbGVmdF9qb2luKGNpdHlfbm1kcykgJT4lIG11dGF0ZShjbHVzdGVyID0gYXMuZmFjdG9yKGNsdXN0ZXIpKQp9CmBgYAoKYGBge3J9CnBsb3Rfbm1kc19jbHVzdGVycyA9IGZ1bmN0aW9uKGNsdXN0ZXJfY2l0aWVzKSB7CiAgY2x1c3Rlcl9jaXRpZXMgJT4lIGRwbHlyOjpzZWxlY3QoY2l0eV9pZCwgY2l0eV9uYW1lLCBOTURTMSwgTk1EUzIsIGNsdXN0ZXIpICU+JSBkaXN0aW5jdCgpICU+JQogIGdncGxvdChhZXMoeCA9IE5NRFMxLCB5ID0gTk1EUzIsIGNvbG91ciA9IGNsdXN0ZXIpKSArIGdlb21fcG9pbnQoKSArIGdlb21fbGFiZWxfcmVwZWwoYWVzKGxhYmVsID0gY2l0eV9uYW1lKSkKfQpgYGAKCmBgYHtyfQpwbG90X2NpdHlfY2x1c3RlciA9IGZ1bmN0aW9uKGNpdHlfY2x1c3Rlcl9kYXRhX21ldHJpY3MsIHRpdGxlKSB7CiAgc3BlY2llc19pbl9jbHVzdGVyID0gY29tbXVuaXRpZXMgJT4lIAogICAgZmlsdGVyKGNpdHlfaWQgJWluJSBjaXR5X2NsdXN0ZXJfZGF0YV9tZXRyaWNzJGNpdHlfaWQpICU+JSAKICAgIGdyb3VwX2J5KGpldHpfc3BlY2llc19uYW1lLCBjaXR5X25hbWUsIHByZXNlbnRfdXJiYW5faGlnaCwgcHJlc2VudF91cmJhbl9tZWQsIHByZXNlbnRfdXJiYW5fbG93KSAlPiUgCiAgICBzdW1tYXJpc2UoKSAlPiUKICAgIG11dGF0ZShwcmVzZW50X3Njb3JlID0gcHJlc2VudF91cmJhbl9oaWdoICsgcHJlc2VudF91cmJhbl9tZWQgKyBwcmVzZW50X3VyYmFuX2xvdykKCiAgc3BlY2llc19pbl9jbHVzdGVyJHByZXNlbnRfc2NvcmUgPSBmYWN0b3Ioc3BlY2llc19pbl9jbHVzdGVyJHByZXNlbnRfc2NvcmUsIGxldmVscyA9IGMoMCwgMSwgMiwgMyksIGxhYmVscyA9IGMoJ1JlZ2lvbmFsIFBvb2wnLCAnTG93IFRocmVzaG9sZCcsICdNZWRpdW0gVGhyZXNob2xkJywgJ0hpZ2ggVGhyZXNob2xkJykpCiAgCiAgdHJlZV9jcm9wcGVkIDwtIGxhZGRlcml6ZShkcm9wLnRpcChwaHlsb190cmVlLCBzZXRkaWZmKHBoeWxvX3RyZWUkdGlwLmxhYmVsLCBzcGVjaWVzX2luX2NsdXN0ZXIkamV0el9zcGVjaWVzX25hbWUpKSkKICAgIAogIGdnX3RyZWUgPSBnZ3RyZWUodHJlZV9jcm9wcGVkKQogIAogIGdnX3ByZXNlbmNlID0gZ2dwbG90KHNwZWNpZXNfaW5fY2x1c3RlciwgYWVzKHg9Y2l0eV9uYW1lLCB5PWpldHpfc3BlY2llc19uYW1lKSkgKyAKICAgICAgICAgIGdlb21fdGlsZShhZXMoZmlsbD1wcmVzZW50X3Njb3JlKSkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJncmVlbiIsICJ5ZWxsb3ciLCAib3JhbmdlIiwgInJlZCIpKSArIAogICAgICAgICAgdGhlbWVfbWluaW1hbCgpICsgeGxhYihOVUxMKSArIHlsYWIoTlVMTCkgKyAKICAgICAgICAgIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSkgKyAKICAgICAgICAgIGxhYnMoZmlsbD0nUHJlc2VuY2UnKQogIAogIHNwZWNpZXNfaW5fY2x1c3Rlcl90cmFpdHMgPSBmZXRjaF9ub3JtYWxpc2VkX3RyYWl0cyhzcGVjaWVzX2luX2NsdXN0ZXIkamV0el9zcGVjaWVzX25hbWUpCiAgCiAgZ2dfdHJhaXRzID0gZ2dwbG90KHNwZWNpZXNfaW5fY2x1c3Rlcl90cmFpdHMsIGFlcyh4ID0gdHJhaXQsIHkgPSBqZXR6X3NwZWNpZXNfbmFtZSwgc2l6ZSA9IG5vcm1hbGlzZWRfdmFsdWUpKSArIGdlb21fcG9pbnQoKSArIHRoZW1lX21pbmltYWwoKSArIHRoZW1lKGF4aXMudGV4dC54ID0gZWxlbWVudF90ZXh0KGFuZ2xlID0gOTAsIHZqdXN0ID0gMC41LCBoanVzdD0xKSwgYXhpcy50ZXh0Lnk9ZWxlbWVudF9ibGFuaygpKSArIHhsYWIoTlVMTCkgKyB5bGFiKE5VTEwpICsgbGFicyhzaXplID0gIk5vcm1hbGlzZWQgVmFsdWUiKQogIAogIGdnX2NpdGllc19tbnRkID0gZ2dwbG90KGNpdHlfY2x1c3Rlcl9kYXRhX21ldHJpY3MsIGFlcyh4ID0gY2l0eV9uYW1lLCB5ID0gbW50ZF9ub3JtYWxpc2VkLCBmaWxsID0gaXNfdXJiYW5fdGhyZXNob2xkKSkgKyBnZW9tX2JhcihzdGF0ID0gImlkZW50aXR5IiwgcG9zaXRpb24gPSBwb3NpdGlvbl9kb2RnZShwcmVzZXJ2ZSA9ICJzaW5nbGUiKSkgKyBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXMgPSBjKCJ5ZWxsb3ciLCAib3JhbmdlIiwgInJlZCIpKSAgKyB0aGVtZV9taW5pbWFsKCkgKyB0aGVtZShsZWdlbmQucG9zaXRpb24gPSAibm9uZSIsIGF4aXMudGV4dC54PWVsZW1lbnRfYmxhbmsoKSkgKyB4bGFiKE5VTEwpICsgeWxhYigiTU5URCIpICsgeWxpbSgwLCAxKQogIAogIGdnX2NpdGllc19mZCA9IGdncGxvdChjaXR5X2NsdXN0ZXJfZGF0YV9tZXRyaWNzLCBhZXMoeCA9IGNpdHlfbmFtZSwgeSA9IGZkX25vcm1hbGlzZWQsIGZpbGwgPSBpc191cmJhbl90aHJlc2hvbGQpKSArIGdlb21fYmFyKHN0YXQgPSAiaWRlbnRpdHkiLCBwb3NpdGlvbiA9IHBvc2l0aW9uX2RvZGdlKHByZXNlcnZlID0gInNpbmdsZSIpKSArIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcyA9IGMoInllbGxvdyIsICJvcmFuZ2UiLCAicmVkIikpICsgdGhlbWVfbWluaW1hbCgpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiLCBheGlzLnRleHQueD1lbGVtZW50X2JsYW5rKCkpICsgeGxhYihOVUxMKSArIHlsYWIoIkZEIikgKyB5bGltKDAsIDEpCiAgCiAgZ2dfdGl0bGUgPSBnZ3Bsb3QoKSArIGxhYnModGl0bGUgPSB0aXRsZSkgKyB0aGVtZV9taW5pbWFsKCkKICAKICBnZ19wcmVzZW5jZSAlPiUgaW5zZXJ0X3RvcChnZ19jaXRpZXNfbW50ZCwgaGVpZ2h0ID0gMC41KSAlPiUgaW5zZXJ0X3RvcChnZ19jaXRpZXNfZmQsIGhlaWdodCA9IDAuNSkgJT4lIGluc2VydF9sZWZ0KGdnX3RyZWUsIHdpZHRoID0gMC43NSkgJT4lIGluc2VydF9yaWdodChnZ190cmFpdHMsIHdpZHRoID0gMC41KSAlPiUgaW5zZXJ0X3RvcChnZ190aXRsZSwgaGVpZ2h0ID0gMC4wNikKfQpgYGAKCmBgYHtyfQpSRUdJT05fREVFUF9ESVZFX0ZJR1VSRVNfT1VUUFVUID0gbWtkaXIoRklHVVJFU19PVVRQVVRfRElSLCAnYXBwZW5kaXhfcmVnaW9uYWxfZGVlcF9kaXZlJykKYGBgCgojIyBOZWFyY3RpYwpgYGB7cn0KbmVhcmN0aWNfY2l0aWVzX2NvbW11bml0eV9kYXRhID0gY29tbXVuaXR5X2RhdGFfbWV0cmljcyAlPiUgZmlsdGVyKGNvcmVfcmVhbG0gPT0gJ05lYXJjdGljJykKbmVhcmN0aWNfY2l0aWVzX2NvbW11bml0eV9kYXRhICU+JSBkcGx5cjo6c2VsZWN0KGNpdHlfbmFtZSkgJT4lIGRpc3RpbmN0KCkgJT4lIGFzLmxpc3QoKQpgYGAKCmBgYHtyfQpuZWFyY3RpY19jaXRpZXNfbm1kcyA9IGNvbW11bml0eV9ubWRzKGNvbW11bml0aWVzICU+JSBmaWx0ZXIoY2l0eV9pZCAlaW4lIG5lYXJjdGljX2NpdGllc19jb21tdW5pdHlfZGF0YSRjaXR5X2lkKSkgCm5lYXJjdGljX2NpdGllc19ubWRzCmBgYAoKYGBge3J9CnNjcmVlX3Bsb3QobmVhcmN0aWNfY2l0aWVzX25tZHMpCmBgYAoKYGBge3J9Cm5lYXJjdGljX2NpdGllcyA9IGNsdXN0ZXJfY2l0aWVzKGNpdHlfbm1kcyA9IG5lYXJjdGljX2NpdGllc19ubWRzLCBjaXRpZXNfY29tbXVuaXR5X2RhdGEgPSBuZWFyY3RpY19jaXRpZXNfY29tbXVuaXR5X2RhdGEsIGNlbnRlcnMgPSA1KQpgYGAKCmBgYHtyfQpwbG90X25tZHNfY2x1c3RlcnMobmVhcmN0aWNfY2l0aWVzKQpgYGAKCmBgYHtyfQpuZWFyY3RpY19iaW9tZXMgPSBzdF9jcm9wKHJlc29sdmVbcmVzb2x2ZSRSRUFMTSA9PSAnTmVhcmN0aWMnLGMoJ1JFQUxNJyldLCB4bWluID0gLTIyMCwgeW1pbiA9IDAsIHhtYXggPSAwLCB5bWF4ID0gNzApCiAKZ2dwbG90KCkgKyAKICBnZW9tX3NmKGRhdGEgPSBuZWFyY3RpY19iaW9tZXMsIGFlcyhnZW9tZXRyeSA9IGdlb21ldHJ5KSkgKyAKICBnZW9tX3NmKGRhdGEgPSBuZWFyY3RpY19jaXRpZXMsIGFlcyhnZW9tZXRyeSA9IGdlb21ldHJ5LCBjb2xvciA9IGNsdXN0ZXIpKQoKZ2dzYXZlKGZpbGVuYW1lKFJFR0lPTl9ERUVQX0RJVkVfRklHVVJFU19PVVRQVVQsICduZWFydGljX2NsdXN0ZXJzLmpwZycpKQpgYGAKCiMjIyBOZWFydGljIENsdXN0ZXIgMQpgYGB7cn0KbmVhcmN0aWNfY2l0aWVzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAxKSAlPiUgcGxvdF9jaXR5X2NsdXN0ZXIoJ05lYXJ0aWMgY2x1c3RlciAxJykKZ2dzYXZlKGZpbGVuYW1lKFJFR0lPTl9ERUVQX0RJVkVfRklHVVJFU19PVVRQVVQsICduZWFydGljX2NsdXN0ZXIxLmpwZycpKQpgYGAKCiMjIyBOZWFydGljIENsdXN0ZXIgMgpgYGB7cn0KbmVhcmN0aWNfY2l0aWVzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAyKSAlPiUgcGxvdF9jaXR5X2NsdXN0ZXIoJ05lYXJ0aWMgY2x1c3RlciAyJykKZ2dzYXZlKGZpbGVuYW1lKFJFR0lPTl9ERUVQX0RJVkVfRklHVVJFU19PVVRQVVQsICduZWFydGljX2NsdXN0ZXIyLmpwZycpKQpgYGAKCiMjIyBOZWFydGljIENsdXN0ZXIgMwpgYGB7cn0KbmVhcmN0aWNfY2l0aWVzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAzKSAlPiUgcGxvdF9jaXR5X2NsdXN0ZXIoJ05lYXJ0aWMgY2x1c3RlciAzJykKZ2dzYXZlKGZpbGVuYW1lKFJFR0lPTl9ERUVQX0RJVkVfRklHVVJFU19PVVRQVVQsICduZWFydGljX2NsdXN0ZXIzLmpwZycpKQpgYGAKIyMjIE5lYXJ0aWMgQ2x1c3RlciA0CmBgYHtyfQpuZWFyY3RpY19jaXRpZXMgJT4lIGZpbHRlcihjbHVzdGVyID09IDQpICU+JSBwbG90X2NpdHlfY2x1c3RlcignTmVhcnRpYyBjbHVzdGVyIDQnKQpnZ3NhdmUoZmlsZW5hbWUoUkVHSU9OX0RFRVBfRElWRV9GSUdVUkVTX09VVFBVVCwgJ25lYXJ0aWNfY2x1c3RlcjQuanBnJykpCmBgYAoKIyMjIE5lYXJ0aWMgQ2x1c3RlciA1CmBgYHtyfQpuZWFyY3RpY19jaXRpZXMgJT4lIGZpbHRlcihjbHVzdGVyID09IDUpICU+JSBwbG90X2NpdHlfY2x1c3RlcignTmVhcnRpYyBjbHVzdGVyIDUnKQpnZ3NhdmUoZmlsZW5hbWUoUkVHSU9OX0RFRVBfRElWRV9GSUdVUkVTX09VVFBVVCwgJ25lYXJ0aWNfY2x1c3RlcjUuanBnJykpCmBgYAoKIyMgTmVvdHJvcGljCmBgYHtyfQpuZW90cm9waWNfY2l0aWVzX2NvbW11bml0eV9kYXRhID0gY29tbXVuaXR5X2RhdGFfbWV0cmljcyAlPiUgZmlsdGVyKGNvcmVfcmVhbG0gPT0gJ05lb3Ryb3BpYycpCm5lb3Ryb3BpY19jaXRpZXNfY29tbXVuaXR5X2RhdGEgJT4lIGRwbHlyOjpzZWxlY3QoY2l0eV9uYW1lKSAlPiUgZGlzdGluY3QoKSAlPiUgYXMubGlzdCgpCmBgYAoKYGBge3J9Cm5lb3Ryb3BpY19jaXRpZXNfbm1kcyA9IGNvbW11bml0eV9ubWRzKGNvbW11bml0aWVzICU+JSBmaWx0ZXIoY2l0eV9pZCAlaW4lIG5lb3Ryb3BpY19jaXRpZXNfY29tbXVuaXR5X2RhdGEkY2l0eV9pZCkpIApuZW90cm9waWNfY2l0aWVzX25tZHMKYGBgCgpgYGB7cn0Kc2NyZWVfcGxvdChuZW90cm9waWNfY2l0aWVzX25tZHMpCmBgYAoKYGBge3J9Cm5lb3Ryb3BpY19jaXRpZXMgPSBjbHVzdGVyX2NpdGllcyhjaXR5X25tZHMgPSBuZW90cm9waWNfY2l0aWVzX25tZHMsIGNpdGllc19jb21tdW5pdHlfZGF0YSA9IG5lb3Ryb3BpY19jaXRpZXNfY29tbXVuaXR5X2RhdGEsIGNlbnRlcnMgPSA1KQpgYGAKCmBgYHtyfQpwbG90X25tZHNfY2x1c3RlcnMobmVvdHJvcGljX2NpdGllcykKYGBgCgpgYGB7cn0KbmVvdHJvcGljX2Jpb21lcyA9IHJlc29sdmVbcmVzb2x2ZSRSRUFMTSA9PSAnTmVvdHJvcGljJyxjKCdSRUFMTScpXQogCmdncGxvdCgpICsgCiAgZ2VvbV9zZihkYXRhID0gbmVvdHJvcGljX2Jpb21lcywgYWVzKGdlb21ldHJ5ID0gZ2VvbWV0cnkpKSArIAogIGdlb21fc2YoZGF0YSA9IG5lb3Ryb3BpY19jaXRpZXMsIGFlcyhnZW9tZXRyeSA9IGdlb21ldHJ5LCBjb2xvciA9IGNsdXN0ZXIpKQpnZ3NhdmUoZmlsZW5hbWUoUkVHSU9OX0RFRVBfRElWRV9GSUdVUkVTX09VVFBVVCwgJ25lb3Ryb3BpY19jbHVzdGVycy5qcGcnKSkKYGBgCgojIyMgTmVvdHJvcGljIENsdXN0ZXIgMQpgYGB7cn0KbmVvdHJvcGljX2NpdGllcyAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gMSkgJT4lIHBsb3RfY2l0eV9jbHVzdGVyKCdOZW90cm9waWMgY2x1c3RlciAxJykKZ2dzYXZlKGZpbGVuYW1lKFJFR0lPTl9ERUVQX0RJVkVfRklHVVJFU19PVVRQVVQsICduZW90cm9waWNfY2x1c3RlcjEuanBnJykpCmBgYAoKIyMjIE5lb3Ryb3BpYyBDbHVzdGVyIDIKYGBge3J9Cm5lb3Ryb3BpY19jaXRpZXMgJT4lIGZpbHRlcihjbHVzdGVyID09IDIpICU+JSBwbG90X2NpdHlfY2x1c3RlcignTmVvdHJvcGljIGNsdXN0ZXIgMicpCmdnc2F2ZShmaWxlbmFtZShSRUdJT05fREVFUF9ESVZFX0ZJR1VSRVNfT1VUUFVULCAnbmVvdHJvcGljX2NsdXN0ZXIyLmpwZycpKQpgYGAKCiMjIyBOZW90cm9waWMgQ2x1c3RlciAzCmBgYHtyfQpuZW90cm9waWNfY2l0aWVzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAzKSAlPiUgcGxvdF9jaXR5X2NsdXN0ZXIoJ05lb3Ryb3BpYyBjbHVzdGVyIDMnKQpnZ3NhdmUoZmlsZW5hbWUoUkVHSU9OX0RFRVBfRElWRV9GSUdVUkVTX09VVFBVVCwgJ25lb3Ryb3BpY19jbHVzdGVyMy5qcGcnKSkKYGBgCgojIyMgTmVvdHJvcGljIENsdXN0ZXIgNApgYGB7cn0KbmVvdHJvcGljX2NpdGllcyAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gNCkgJT4lIHBsb3RfY2l0eV9jbHVzdGVyKCdOZW90cm9waWMgY2x1c3RlciA0JykKZ2dzYXZlKGZpbGVuYW1lKFJFR0lPTl9ERUVQX0RJVkVfRklHVVJFU19PVVRQVVQsICduZW90cm9waWNfY2x1c3RlcjQuanBnJykpCmBgYAoKIyMjIE5lb3Ryb3BpYyBDbHVzdGVyIDUKYGBge3J9Cm5lb3Ryb3BpY19jaXRpZXMgJT4lIGZpbHRlcihjbHVzdGVyID09IDUpICU+JSBwbG90X2NpdHlfY2x1c3RlcignTmVvdHJvcGljIGNsdXN0ZXIgNScpCmdnc2F2ZShmaWxlbmFtZShSRUdJT05fREVFUF9ESVZFX0ZJR1VSRVNfT1VUUFVULCAnbmVvdHJvcGljX2NsdXN0ZXI1LmpwZycpKQpgYGAKCiMjIFBhbGVhcmN0aWMKYGBge3J9CnBhbGVhcmN0aWNfY2l0aWVzX2NvbW11bml0eV9kYXRhID0gY29tbXVuaXR5X2RhdGFfbWV0cmljcyAlPiUgZmlsdGVyKGNvcmVfcmVhbG0gPT0gJ1BhbGVhcmN0aWMnKQpwYWxlYXJjdGljX2NpdGllc19jb21tdW5pdHlfZGF0YSAlPiUgZHBseXI6OnNlbGVjdChjaXR5X25hbWUpICU+JSBkaXN0aW5jdCgpICU+JSBhcy5saXN0KCkKYGBgCgpgYGB7cn0KcGFsZWFyY3RpY19jaXRpZXNfbm1kcyA9IGNvbW11bml0eV9ubWRzKGNvbW11bml0aWVzICU+JSBmaWx0ZXIoY2l0eV9pZCAlaW4lIHBhbGVhcmN0aWNfY2l0aWVzX2NvbW11bml0eV9kYXRhJGNpdHlfaWQpKSAKcGFsZWFyY3RpY19jaXRpZXNfbm1kcwpgYGAKCmBgYHtyfQpzY3JlZV9wbG90KHBhbGVhcmN0aWNfY2l0aWVzX25tZHMpCmBgYAoKYGBge3J9CnBhbGVhcmN0aWNfY2l0aWVzID0gY2x1c3Rlcl9jaXRpZXMoY2l0eV9ubWRzID0gcGFsZWFyY3RpY19jaXRpZXNfbm1kcywgY2l0aWVzX2NvbW11bml0eV9kYXRhID0gcGFsZWFyY3RpY19jaXRpZXNfY29tbXVuaXR5X2RhdGEsIGNlbnRlcnMgPSA2KQpgYGAKCmBgYHtyfQpwbG90X25tZHNfY2x1c3RlcnMocGFsZWFyY3RpY19jaXRpZXMpCmBgYAoKYGBge3J9CnBhbGVhcmN0aWNfYmlvbWVzID0gcmVzb2x2ZVtyZXNvbHZlJFJFQUxNID09ICdQYWxlYXJjdGljJyxjKCdSRUFMTScpXQogCmdncGxvdCgpICsgCiAgZ2VvbV9zZihkYXRhID0gcGFsZWFyY3RpY19iaW9tZXMsIGFlcyhnZW9tZXRyeSA9IGdlb21ldHJ5KSkgKyAKICBnZW9tX3NmKGRhdGEgPSBwYWxlYXJjdGljX2NpdGllcywgYWVzKGdlb21ldHJ5ID0gZ2VvbWV0cnksIGNvbG9yID0gY2x1c3RlcikpCmdnc2F2ZShmaWxlbmFtZShSRUdJT05fREVFUF9ESVZFX0ZJR1VSRVNfT1VUUFVULCAncGFsZWFyY3RpY19jbHVzdGVycy5qcGcnKSkKYGBgCgojIyMgUGFsZWFyY3RpYyBDbHVzdGVyIDEKYGBge3J9CnBhbGVhcmN0aWNfY2l0aWVzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAxKSAlPiUgcGxvdF9jaXR5X2NsdXN0ZXIoJ1BhbGVhcmN0aWMgY2x1c3RlciAxJykKZ2dzYXZlKGZpbGVuYW1lKFJFR0lPTl9ERUVQX0RJVkVfRklHVVJFU19PVVRQVVQsICdwYWxlYXJjdGljX2NsdXN0ZXIxLmpwZycpKQpgYGAKCiMjIyBQYWxlYXJjdGljIENsdXN0ZXIgMgpgYGB7cn0KcGFsZWFyY3RpY19jaXRpZXMgJT4lIGZpbHRlcihjbHVzdGVyID09IDIpICU+JSBwbG90X2NpdHlfY2x1c3RlcignUGFsZWFyY3RpYyBjbHVzdGVyIDInKQpnZ3NhdmUoZmlsZW5hbWUoUkVHSU9OX0RFRVBfRElWRV9GSUdVUkVTX09VVFBVVCwgJ3BhbGVhcmN0aWNfY2x1c3RlcjIuanBnJykpCmBgYAoKIyMjIFBhbGVhcmN0aWMgQ2x1c3RlciAzCmBgYHtyLCBmaWcud2lkdGg9MTQsIGZpZy5oZWlnaHQ9Nn0KcGFsZWFyY3RpY19jaXRpZXMgJT4lIGZpbHRlcihjbHVzdGVyID09IDMpICU+JSBwbG90X2NpdHlfY2x1c3RlcignUGFsZWFyY3RpYyBjbHVzdGVyIDMnKQpnZ3NhdmUoZmlsZW5hbWUoUkVHSU9OX0RFRVBfRElWRV9GSUdVUkVTX09VVFBVVCwgJ3BhbGVhcmN0aWNfY2x1c3RlcjMuanBnJykpCmBgYAoKIyMjIFBhbGVhcmN0aWMgQ2x1c3RlciA0CmBgYHtyfQpwYWxlYXJjdGljX2NpdGllcyAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gNCkgJT4lIHBsb3RfY2l0eV9jbHVzdGVyKCdQYWxlYXJjdGljIGNsdXN0ZXIgNCcpCmdnc2F2ZShmaWxlbmFtZShSRUdJT05fREVFUF9ESVZFX0ZJR1VSRVNfT1VUUFVULCAncGFsZWFyY3RpY19jbHVzdGVyNC5qcGcnKSkKYGBgCgojIyMgUGFsZWFyY3RpYyBDbHVzdGVyIDUKYGBge3J9CnBhbGVhcmN0aWNfY2l0aWVzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSA1KSAlPiUgcGxvdF9jaXR5X2NsdXN0ZXIoJ1BhbGVhcmN0aWMgY2x1c3RlciA1JykKZ2dzYXZlKGZpbGVuYW1lKFJFR0lPTl9ERUVQX0RJVkVfRklHVVJFU19PVVRQVVQsICdwYWxlYXJjdGljX2NsdXN0ZXI1LmpwZycpKQpgYGAKCiMjIyBQYWxlYXJjdGljIENsdXN0ZXIgNgpgYGB7cn0KcGFsZWFyY3RpY19jaXRpZXMgJT4lIGZpbHRlcihjbHVzdGVyID09IDYpICU+JSBwbG90X2NpdHlfY2x1c3RlcignUGFsZWFyY3RpYyBjbHVzdGVyIDYnKQpnZ3NhdmUoZmlsZW5hbWUoUkVHSU9OX0RFRVBfRElWRV9GSUdVUkVTX09VVFBVVCwgJ3BhbGVhcmN0aWNfY2x1c3RlcjYuanBnJykpCmBgYAoKIyMgQWZyb3Ryb3BpYwpgYGB7cn0KYWZyb3Ryb3BpY19jaXRpZXNfY29tbXVuaXR5X2RhdGEgPSBjb21tdW5pdHlfZGF0YV9tZXRyaWNzICU+JSBmaWx0ZXIoY29yZV9yZWFsbSA9PSAnQWZyb3Ryb3BpYycpCmFmcm90cm9waWNfY2l0aWVzX2NvbW11bml0eV9kYXRhICU+JSBkcGx5cjo6c2VsZWN0KGNpdHlfbmFtZSkgJT4lIGRpc3RpbmN0KCkgJT4lIGFzLmxpc3QoKQpgYGAKCmBgYHtyfQphZnJvdHJvcGljX2NpdGllc19ubWRzID0gY29tbXVuaXR5X25tZHMoY29tbXVuaXRpZXMgJT4lIGZpbHRlcihjaXR5X2lkICVpbiUgYWZyb3Ryb3BpY19jaXRpZXNfY29tbXVuaXR5X2RhdGEkY2l0eV9pZCkpIAphZnJvdHJvcGljX2NpdGllc19ubWRzCmBgYAoKYGBge3J9CnNjcmVlX3Bsb3QoYWZyb3Ryb3BpY19jaXRpZXNfbm1kcykKYGBgCgpgYGB7cn0KYWZyb3Ryb3BpY19jaXRpZXMgPSBjbHVzdGVyX2NpdGllcyhjaXR5X25tZHMgPSBhZnJvdHJvcGljX2NpdGllc19ubWRzLCBjaXRpZXNfY29tbXVuaXR5X2RhdGEgPSBhZnJvdHJvcGljX2NpdGllc19jb21tdW5pdHlfZGF0YSwgY2VudGVycyA9IDQpCmBgYAoKYGBge3J9CnBsb3Rfbm1kc19jbHVzdGVycyhhZnJvdHJvcGljX2NpdGllcykKYGBgCgpgYGB7cn0KYWZyb3Ryb3BpY19iaW9tZXMgPSByZXNvbHZlW3Jlc29sdmUkUkVBTE0gPT0gJ0Fmcm90cm9waWMnLGMoJ1JFQUxNJyldCiAKZ2dwbG90KCkgKyAKICBnZW9tX3NmKGRhdGEgPSBhZnJvdHJvcGljX2Jpb21lcywgYWVzKGdlb21ldHJ5ID0gZ2VvbWV0cnkpKSArIAogIGdlb21fc2YoZGF0YSA9IGFmcm90cm9waWNfY2l0aWVzLCBhZXMoZ2VvbWV0cnkgPSBnZW9tZXRyeSwgY29sb3IgPSBjbHVzdGVyKSkKZ2dzYXZlKGZpbGVuYW1lKFJFR0lPTl9ERUVQX0RJVkVfRklHVVJFU19PVVRQVVQsICdhZnJvdHJvcGljX2NsdXN0ZXJzLmpwZycpKQpgYGAKCiMjIyBBZnJvdHJvcGljIENsdXN0ZXIgMQpgYGB7cn0KYWZyb3Ryb3BpY19jaXRpZXMgJT4lIGZpbHRlcihjbHVzdGVyID09IDEpICU+JSBwbG90X2NpdHlfY2x1c3RlcignQWZyb3Ryb3BpYyBjbHVzdGVyIDEnKQpnZ3NhdmUoZmlsZW5hbWUoUkVHSU9OX0RFRVBfRElWRV9GSUdVUkVTX09VVFBVVCwgJ2Fmcm90cm9waWNfY2x1c3RlcjEuanBnJykpCmBgYAoKIyMjIEFmcm90cm9waWMgQ2x1c3RlciAyCmBgYHtyfQphZnJvdHJvcGljX2NpdGllcyAlPiUgZmlsdGVyKGNsdXN0ZXIgPT0gMikgJT4lIHBsb3RfY2l0eV9jbHVzdGVyKCdBZnJvdHJvcGljIGNsdXN0ZXIgMicpCmdnc2F2ZShmaWxlbmFtZShSRUdJT05fREVFUF9ESVZFX0ZJR1VSRVNfT1VUUFVULCAnYWZyb3Ryb3BpY19jbHVzdGVyMi5qcGcnKSkKYGBgCgojIyMgQWZyb3Ryb3BpYyBDbHVzdGVyIDMKYGBge3J9CmFmcm90cm9waWNfY2l0aWVzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAzKSAlPiUgcGxvdF9jaXR5X2NsdXN0ZXIoJ0Fmcm90cm9waWMgY2x1c3RlciAzJykKZ2dzYXZlKGZpbGVuYW1lKFJFR0lPTl9ERUVQX0RJVkVfRklHVVJFU19PVVRQVVQsICdhZnJvdHJvcGljX2NsdXN0ZXIzLmpwZycpKQpgYGAKCiMjIyBBZnJvdHJvcGljIENsdXN0ZXIgNApgYGB7cn0KYWZyb3Ryb3BpY19jaXRpZXMgJT4lIGZpbHRlcihjbHVzdGVyID09IDQpICU+JSBwbG90X2NpdHlfY2x1c3RlcignQWZyb3Ryb3BpYyBjbHVzdGVyIDQnKQpnZ3NhdmUoZmlsZW5hbWUoUkVHSU9OX0RFRVBfRElWRV9GSUdVUkVTX09VVFBVVCwgJ2Fmcm90cm9waWNfY2x1c3RlcjQuanBnJykpCmBgYAoKIyMgSW5kb21hbGF5YW4KYGBge3J9CmluZG9tYWxheWFuX2NpdGllc19jb21tdW5pdHlfZGF0YSA9IGNvbW11bml0eV9kYXRhX21ldHJpY3MgJT4lIGZpbHRlcihjb3JlX3JlYWxtID09ICdJbmRvbWFsYXlhbicpCmluZG9tYWxheWFuX2NpdGllc19jb21tdW5pdHlfZGF0YSAlPiUgZHBseXI6OnNlbGVjdChjaXR5X25hbWUpICU+JSBkaXN0aW5jdCgpICU+JSBhcy5saXN0KCkKYGBgCgpgYGB7cn0KaW5kb21hbGF5YW5fY2l0aWVzX25tZHMgPSBjb21tdW5pdHlfbm1kcyhjb21tdW5pdGllcyAlPiUgZmlsdGVyKGNpdHlfaWQgJWluJSBpbmRvbWFsYXlhbl9jaXRpZXNfY29tbXVuaXR5X2RhdGEkY2l0eV9pZCkpIAppbmRvbWFsYXlhbl9jaXRpZXNfbm1kcwpgYGAKCmBgYHtyfQpzY3JlZV9wbG90KGluZG9tYWxheWFuX2NpdGllc19ubWRzKQpgYGAKCmBgYHtyfQppbmRvbWFsYXlhbl9jaXRpZXMgPSBjbHVzdGVyX2NpdGllcyhjaXR5X25tZHMgPSBpbmRvbWFsYXlhbl9jaXRpZXNfbm1kcywgY2l0aWVzX2NvbW11bml0eV9kYXRhID0gaW5kb21hbGF5YW5fY2l0aWVzX2NvbW11bml0eV9kYXRhLCBjZW50ZXJzID0gNikKYGBgCgpgYGB7cn0KcGxvdF9ubWRzX2NsdXN0ZXJzKGluZG9tYWxheWFuX2NpdGllcykKYGBgCgpgYGB7cn0KaW5kb21hbGF5YW5fYmlvbWVzID0gcmVzb2x2ZVtyZXNvbHZlJFJFQUxNID09ICdJbmRvbWFsYXlhbicsYygnUkVBTE0nKV0KIApnZ3Bsb3QoKSArIAogIGdlb21fc2YoZGF0YSA9IGluZG9tYWxheWFuX2Jpb21lcywgYWVzKGdlb21ldHJ5ID0gZ2VvbWV0cnkpKSArIAogIGdlb21fc2YoZGF0YSA9IGluZG9tYWxheWFuX2NpdGllcywgYWVzKGdlb21ldHJ5ID0gZ2VvbWV0cnksIGNvbG9yID0gY2x1c3RlcikpCmdnc2F2ZShmaWxlbmFtZShSRUdJT05fREVFUF9ESVZFX0ZJR1VSRVNfT1VUUFVULCAnaW5kb21hbGF5YW5fY2x1c3RlcnMuanBnJykpCmBgYAoKIyMjIEluZG9tYWxheWFuIENsdXN0ZXIgMQpgYGB7cn0KaW5kb21hbGF5YW5fY2l0aWVzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAxKSAlPiUgcGxvdF9jaXR5X2NsdXN0ZXIoJ0luZG9tYWxheWFuIGNsdXN0ZXIgMScpCmdnc2F2ZShmaWxlbmFtZShSRUdJT05fREVFUF9ESVZFX0ZJR1VSRVNfT1VUUFVULCAnaW5kb21hbGF5YW5fY2x1c3RlcjEuanBnJykpCmBgYAoKIyMjIEluZG9tYWxheWFuIENsdXN0ZXIgMgpgYGB7cn0KaW5kb21hbGF5YW5fY2l0aWVzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAyKSAlPiUgcGxvdF9jaXR5X2NsdXN0ZXIoJ0luZG9tYWxheWFuIGNsdXN0ZXIgMicpCmdnc2F2ZShmaWxlbmFtZShSRUdJT05fREVFUF9ESVZFX0ZJR1VSRVNfT1VUUFVULCAnaW5kb21hbGF5YW5fY2x1c3RlcjIuanBnJykpCmBgYAoKIyMjIEluZG9tYWxheWFuIENsdXN0ZXIgMwpgYGB7cn0KaW5kb21hbGF5YW5fY2l0aWVzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSAzKSAlPiUgcGxvdF9jaXR5X2NsdXN0ZXIoJ0luZG9tYWxheWFuIGNsdXN0ZXIgMycpCmdnc2F2ZShmaWxlbmFtZShSRUdJT05fREVFUF9ESVZFX0ZJR1VSRVNfT1VUUFVULCAnaW5kb21hbGF5YW5fY2x1c3RlcjMuanBnJykpCmBgYAoKIyMjIEluZG9tYWxheWFuIENsdXN0ZXIgNApgYGB7cn0KaW5kb21hbGF5YW5fY2l0aWVzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSA0KSAlPiUgcGxvdF9jaXR5X2NsdXN0ZXIoJ0luZG9tYWxheWFuIGNsdXN0ZXIgNCcpCmdnc2F2ZShmaWxlbmFtZShSRUdJT05fREVFUF9ESVZFX0ZJR1VSRVNfT1VUUFVULCAnaW5kb21hbGF5YW5fY2x1c3RlcjQuanBnJykpCmBgYAoKIyMjIEluZG9tYWxheWFuIENsdXN0ZXIgNQpgYGB7cn0KaW5kb21hbGF5YW5fY2l0aWVzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSA1KSAlPiUgcGxvdF9jaXR5X2NsdXN0ZXIoJ0luZG9tYWxheWFuIGNsdXN0ZXIgNScpCmdnc2F2ZShmaWxlbmFtZShSRUdJT05fREVFUF9ESVZFX0ZJR1VSRVNfT1VUUFVULCAnaW5kb21hbGF5YW5fY2x1c3RlcjUuanBnJykpCmBgYAoKIyMjIEluZG9tYWxheWFuIENsdXN0ZXIgNgpgYGB7cn0KaW5kb21hbGF5YW5fY2l0aWVzICU+JSBmaWx0ZXIoY2x1c3RlciA9PSA2KSAlPiUgcGxvdF9jaXR5X2NsdXN0ZXIoJ0luZG9tYWxheWFuIGNsdXN0ZXIgNicpCmdnc2F2ZShmaWxlbmFtZShSRUdJT05fREVFUF9ESVZFX0ZJR1VSRVNfT1VUUFVULCAnaW5kb21hbGF5YW5fY2x1c3RlcjYuanBnJykpCmBgYAoK